using System.Net.WebSockets;
using System.Text;
using FileClient.Controllers.Dtos;
using FileClient.Core;
using FileClient.Core.Client;
using FileCommon.Extensions;
using FileCommon.Models;
using FileEfCore.DbContexts;
using FileEfCore.FileInfo;
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;

namespace FileClient.Controllers;

[ApiController]
[Route("[controller]")]
public class FileController : ControllerBase
{
    private readonly ILogger<FileController> _logger;

    public FileController(ILogger<FileController> logger)
    {
        _logger = logger;
    }

    [HttpPost("Upload")]
    public async Task<IActionResult> Upload([FromBody] FileInput input,
        [FromServices] IFileServiceClient fileServiceClient)
    {
        await fileServiceClient.FileOperation(input.LocalFileName, input.ServerFileName, null, OperationType.Upload);
        return Ok("上传成功");
    }

    [HttpPost("Download")]
    public async Task<IActionResult> Download([FromBody] FileInput input,
        [FromServices] IFileServiceClient fileServiceClient)
    {
        await fileServiceClient.FileOperation(input.LocalFileName, input.ServerFileName, null, OperationType.Download);
        return Ok("下载成功");
    }

    [HttpPost("Operation")]
    public async Task<IActionResult> Download([FromBody] OperationFileInput input,
        [FromServices] IFileServiceClient fileServiceClient)
    {
        await fileServiceClient.FileOperation(input.LocalFileName, input.ServerFileName, null, input.OperationType);
        return Ok("文件传输完成");
    }

    [HttpGet("/ws")]
    public async Task Get([FromServices] IFileServiceClient fileServiceClient)
    {
        if (HttpContext.WebSockets.IsWebSocketRequest)
        {
            using var webSocket = await HttpContext.WebSockets.AcceptWebSocketAsync();
            await HandleReceivedMessage(webSocket, fileServiceClient);
        }
        else
        {
            HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
        }
    }

    private async Task HandleReceivedMessage(WebSocket webSocket, IFileServiceClient fileServiceClient)
    {
        var buffer = new byte[1024 * 10];
        var receiveResult = await webSocket.ReceiveAsync(
            new ArraySegment<byte>(buffer), CancellationToken.None);
        while (!receiveResult.CloseStatus.HasValue)
        {
            try
            {
                var fileInput = ReceivedWsMessage(buffer.ToList().Take(receiveResult.Count).ToArray());
                await fileServiceClient.FileOperation(fileInput.LocalFileName, fileInput.ServerFileName, null,
                    fileInput.OperationType);
                fileServiceClient.OnTransferHandle += async (sender, args) =>
                {
                    var outputLine = JsonConvert.SerializeObject(args);
                    var outputBytes = Encoding.Default.GetBytes(outputLine);
                    if (!webSocket.CloseStatus.HasValue)
                    {
                        await webSocket.SendAsync(
                            new ArraySegment<byte>(outputBytes, 0, outputBytes.Length),
                            receiveResult.MessageType,
                            receiveResult.EndOfMessage,
                            CancellationToken.None);
                    }
                };
                
                fileServiceClient.OnTransferCompleteHandle += async (sender, args) =>
                {
                    var outputLine = JsonConvert.SerializeObject(args);
                    var outputBytes = Encoding.Default.GetBytes(outputLine);
                    if (!webSocket.CloseStatus.HasValue)
                    {
                        await webSocket.SendAsync(
                            new ArraySegment<byte>(outputBytes, 0, outputBytes.Length),
                            receiveResult.MessageType,
                            receiveResult.EndOfMessage,
                            CancellationToken.None);
                    }
                };
                
                fileServiceClient.OnTransferExceptionHandle += async (sender, args) =>
                {
                    if (!webSocket.CloseStatus.HasValue)
                    {
                        var outputLine = JsonConvert.SerializeObject(args);
                        var outputBytes = Encoding.Default.GetBytes(outputLine);
                        await webSocket.SendAsync(
                            new ArraySegment<byte>(outputBytes, 0, outputBytes.Length),
                            receiveResult.MessageType,
                            receiveResult.EndOfMessage,
                            CancellationToken.None);
                    }
                };
            }
            catch (Exception e)
            {
                var wsFileOutput = new WsFileOutput()
                {
                    ProgressStatus = ProgressStatus.Error,
                    ErrorMessage = e.Message
                };
                var outputLine = JsonConvert.SerializeObject(wsFileOutput);
                var outputBytes = Encoding.Default.GetBytes(outputLine);
                await webSocket.SendAsync(
                    new ArraySegment<byte>(outputBytes, 0, outputBytes.Length),
                    receiveResult.MessageType,
                    receiveResult.EndOfMessage,
                    CancellationToken.None);
            }


            receiveResult = await webSocket.ReceiveAsync(
                new ArraySegment<byte>(buffer), CancellationToken.None);
        }

        await webSocket.CloseAsync(
            receiveResult.CloseStatus.Value,
            receiveResult.CloseStatusDescription,
            CancellationToken.None);
    }
    
    private OperationFileInput ReceivedWsMessage(byte[] buffer)
    {
        var receiveLine = Encoding.Default.GetString(buffer);
        if (!receiveLine.IsValidJson())
        {
            throw new Exception("您输入的消息格式不正确");
        }

        var input = JsonConvert.DeserializeObject<OperationFileInput>(receiveLine);
        if (string.IsNullOrWhiteSpace(input.LocalFileName))
        {
            throw new Exception("指定的本地文件完全路径名不允许为空");
        }

        if (string.IsNullOrWhiteSpace(input.ServerFileName))
        {
            throw new Exception("指定的服务端保存的文件相对路径名不允许为空");
        }

        return input;
    }
}